﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using AdvanceLib.Core.Configuration;
using AdvanceLib.ClassExtensions;

namespace AdvanceLib.Extension.XseScript
{
    public class ScriptDatabase
    {
        #region Constructors
        public ScriptDatabase(IEnumerable<string> files)
        {
            this._syntaxFiles = files.ToArray();

            this._nameToDataDictionary = new Dictionary<string, CommandData>();
            this._hexCodeToDataDictionary = new Dictionary<byte, CommandData>();

            this.Commands = new List<CommandData>();
            this.Constants = new Dictionary<string, string>();
            this.Types = new Dictionary<string, int>();

            foreach (string syntaxFile in this._syntaxFiles)
                this.LoadSyntaxFile(syntaxFile);
        }
        #endregion

        #region Fields
        private string[] _syntaxFiles;

        private Dictionary<string, CommandData> _nameToDataDictionary;
        private Dictionary<byte, CommandData> _hexCodeToDataDictionary;
        #endregion

        #region Properties
        public List<CommandData> Commands { get; set; }

        public Dictionary<string, string> Constants { get; set; }
        public Dictionary<string, int> Types { get; set; }
        #endregion

        #region Methods
        public void LoadSyntaxFile(string fileName)
        {
            StreamReader reader = new StreamReader(fileName);
            this.LoadSyntax(reader.ReadToEnd());
        }
        public void LoadSyntax(string content)
        {
            content = content.Replace("\r", string.Empty);
            content = content.Replace("\t", string.Empty);

            string[] lines = Regex.Split(content, "\n");
            int currentLine = 0;

            foreach (string line in lines)
            {
                if (!string.IsNullOrEmpty(line))
                {
                    string[] lineParts = Regex.Split(line, " ");

                    if (lineParts.Length > 0)
                    {
                        string command = lineParts[0];
                        switch (command)
                        {
                            case "#define":
                                string defineTarget = lineParts[1];
                                switch (defineTarget)
                                {
                                    case "type":
                                        if (lineParts.Length >= 4)
                                        {
                                            string key = lineParts[2];
                                            int length = Convert.ToInt32(lineParts[3]);

                                            this.Types.Add(key, length);
                                        }
                                        break;
                                    case "const":
                                        if (lineParts.Length >= 4)
                                        {
                                            string key = lineParts[2];
                                            string value = lineParts[3];

                                            this.Constants.Add(key, value);
                                        }
                                        break;
                                }
                                break;
                            case "#command":
                                if (lineParts.Length >= 3)
                                {
                                    bool parsed = false;

                                    string commandName = lineParts[1];
                                    long hexCode = 0;
                                    List<int> parameters = new List<int>();

                                    for (int i = 3; i < lineParts.Length; i++)
                                    {
                                        string typeName = lineParts[i];
                                        int type = this.GetTypeByName(typeName);

                                        if (type > -1)
                                        {
                                            parameters.Add(type);
                                        }
                                        else
                                        {
                                            long numericType = -1;
                                            parsed = typeName.TryParse(ref numericType);

                                            if (parsed)
                                            {
                                                parameters.Add((int)numericType);
                                            }
                                        }
                                    }

                                    parsed = lineParts[2].TryParse(ref hexCode);

                                    if (parsed)
                                    {
                                        CommandData data = new CommandData()
                                        {
                                            Name = commandName,
                                            HexCode = (byte)hexCode,
                                            Parameters = parameters
                                        };
                                        this.Commands.Add(data);

                                        //Map properties to dictionaries
                                        this._nameToDataDictionary.Add(commandName, data);
                                        this._hexCodeToDataDictionary.Add(data.HexCode, data);
                                    }
                                }
                                break;
                        }
                    }
                }
                currentLine++;
            }
        }
        public CommandData GetCommandDataByName(string name)
        {
            if (this._nameToDataDictionary.ContainsKey(name))
            {
                return this._nameToDataDictionary[name];
            }
            return null;
        }
        public CommandData GetCommandDataByHexCode(byte hexCode)
        {
            if (this._hexCodeToDataDictionary.ContainsKey(hexCode))
            {
                return this._hexCodeToDataDictionary[hexCode];
            }
            return null;
        }
        public bool TypeExists(string typeName)
        {
            return this.Types.ContainsKey(typeName);
        }
        public int GetTypeByName(string typeName)
        {
            if (this.TypeExists(typeName))
                return this.Types[typeName];
            else
                return -1;
        }
        public bool ConstantExists(string constantName)
        {
            return this.Constants.ContainsKey(constantName);
        }
        public string GetConstantByName(string constantName)
        {
            if (this.ConstantExists(constantName))
                return this.Constants[constantName];
            else
                return constantName;
        }
        #endregion
    }
}
